<?php
namespace app\components\pay;
use Yii;
/**
 * 微信支付2.7,只维持正常使用，不加新功能
 */
class Wxpay extends Pay
{
    //发货通知url
    const URL_ORDER_DELIVER = 'https://api.weixin.qq.com/pay/delivernotify?access_token=%s';
    //订单查询ＵＲＬ
    const URL_ORDER_QUERY = 'https://api.weixin.qq.com/pay/orderquery?access_token=%s';
    //小额支付
    const URL_MICROPAY = 'https://api.mch.weixin.qq.com/pay/micropay';
    const URL_MICROPAY_QUERY = 'https://api.mch.weixin.qq.com/pay/orderquery';

    protected $url_micropay = "https://api.mch.weixin.qq.com/pay/micropay";//被扫支付
    protected $url_query = 'https://api.mch.weixin.qq.com/pay/orderquery';//查询


    //预支付
    public function prepay(array $paras){
    }
    //刷卡支付
    public function micropay(array $paras){
        if(!isset($paras['body']))throw new \Exception('body为空',2022017);
        if(!isset($paras['out_trade_no']))throw new \Exception('out_trade_no为空',2022015);
        if(!isset($paras['total_fee']))throw new \Exception('total_fee为空',2022016);
        if(!isset($paras['auth_code']))throw new \Exception('auth_code为空',2022020);
        if(!isset($paras['appid']))$paras['appid'] = $this->appid;
        if(!isset($paras['mch_id']))$paras['mch_id'] = $this->partner_id;
        $paras['nonce_str'] = Func::getcode(32);
        $paras['spbill_create_ip'] = Yii::$app->request->userIP();
        $paras['sign'] = $this->getSign($paras);
        $result = Func::postData($this->url_micropay,Func::arrayToXml($paras));
        if(empty($result)) return $this->micropayquery(['out_trade_no'=> $paras['out_trade_no']]);
        $arr = Func::xmlToArray($result);
        if($arr['result_code'] != 'SUCCESS') return $this->micropayquery(['out_trade_no'=> $paras['out_trade_no']]);
        if($arr['return_code'] != 'SUCCESS') return $this->micropayquery(['out_trade_no'=> $paras['out_trade_no']]);
        return $arr;
    }
    //被扫定单查询,$nums调用次数
    public function micropayquery(array $paras, $nums = 0)
    {
        if($nums > 7) throw new \Exception('查询次数过多,请稍后',2022031);
        $nums ++;
        if(!isset($paras['out_trade_no']))throw new \Exception('out_trade_no为空',2022015);
        $paras['appid'] = $this->appid;
        $paras['mch_id'] = $this->partner_id;
        $paras['nonce_str'] = Mcrypt::getcode(32);
        $paras['sign'] = $this->getMicropaySign($paras);
        $result = Func::postData($this->url_query, Func::arrayToXml($paras));
        if(empty($result)) return call_user_func_array([$this,__METHOD__], [$paras, $nums]);
        $arr = Func::xmlToArray($result);
        if($arr['result_code'] != 'SUCCESS') return call_user_func_array([$this,__METHOD__], [$paras, $nums]);
        if($arr['return_code'] != 'SUCCESS'){
            if($arr['trade_state'] == 'USERPAYING'){
                //刷卡支付遇到需要输入密码时，每５秒查一次，６次后仍未支付则撤销定单。
                if($arr['trade_type'] == 'MICROPAY' && $nums > 6){
                    $this->cancel($paras);
                    throw new \Exception('输入刷卡密码超时,定单已撤销,请重新生成定单并支付',2022032);
                }
                sleep(5);
                return call_user_func_array([$this,__METHOD__], [$paras, $nums]);
            }
            throw new \Exception('微信接口提示:'.$result,2022030);
        }
        return $arr;
    }
    /**
     * 	作用：生成签名
     */
    public function getMicropaySign($Obj)
    {
        foreach ($Obj as $k => $v)
        {
            $Parameters[$k] = (string)$v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters, SORT_STRING);

        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo '【string1】'.$String.'</br>';
        //签名步骤二：在string后加入KEY
        $String = $String."&key=".$this->partner_key;
        //echo "【string2】".$String."</br>";
        //签名步骤三：MD5加密
        $String = md5($String);
        //echo "【string3】 ".$String."</br>";
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        //echo "【result】 ".$result_."</br>";
        return $result_;
    }
    //定单查询,$nums调用次数
    public function orderquery(array $paras, $nums = 1)
    {
        if(!isset($paras['out_trade_no']))throw new \Exception('out_trade_no为空',2022015);
        $paras['appid'] = $this->wxAccountObj->app_id;
        $paras['mch_id'] = $this->paymentObj->partner_id;
        $paras['nonce_str'] = Mcrypt::getcode(32);
        $paras['sign'] = $this->getSign($paras);
        $result = Func::postData($this->url_query, Func::arrayToXml($paras));
        if(empty($result)) $this->orderquery(['out_trade_no'=> $paras['out_trade_no']], $nums++);
        $arr = Func::xmlToArray($result);
        if($arr['result_code'] != 'SUCCESS') $this->orderquery(['out_trade_no'=> $paras['out_trade_no']]);
        if($arr['return_code'] != 'SUCCESS' && $arr['trade_state'] == 'USERPAYING') $this->orderquery(['out_trade_no'=> $paras['out_trade_no']], $nums++);
        if($arr['return_code'] != 'SUCCESS') throw new \Exception('微信接口提示:'.$result,2022030);
        return $arr;


        $arr = array(
            'appid' => $this->wxAccountObj->app_id,
            'mch_id' => $this->paymentObj->partner_id,
            'out_trade_no' => $out_trade_no,
            'nonce_str' => Mcrypt::getcode(32),
        );
        $sign = $this->getSign($arr);
        $arr['sign'] = $sign;
        return Func::postData($this->url_query, Func::arrayToXml($arr));
    }
    /**
     * 微信 退款
     * @param object $orderPay  订单支付数据
     * @param int $refund_fee   退款金额
     * @auth qiaohongbo@kelaile.cn
     */
    public function refund(array $paras) {

        $this->paymentObj = PaymentAccount::getObjByPartnerId($orderPay->payee);
        if(empty($this->paymentObj))throw new \Exception('支付号不存在',2022033);

        $refund_trade_no = Mcrypt::getcode(10);
        $sign_arr = array(
            'appid' => $orderPay->order->enterprise->wxaccount->app_id,
            'mch_id' => $orderPay->payee,
            'nonce_str' => Mcrypt::getcode(32),
            'transaction_id' => $orderPay->trade_id,
            'out_refund_no' => $refund_trade_no,
            'total_fee' => $orderPay->pay_fee,
            'refund_fee' => $refund_fee,
            'op_user_id' => $orderPay->payee,
        );
        $sign_arr['sign'] = $this->getSign($sign_arr);
        $result = Func::postData($this->url_refund, array(
            CURLOPT_POSTFIELDS => Func::arrayToXml($sign_arr),
            CURLOPT_SSLCERT => $this->paymentObj->getPemCert(),
            CURLOPT_SSLKEY => $this->paymentObj->getPemKey(),
        ));

        $arr = Func::xmlToArray($result);
        if ($arr['return_code'] == 'FAIL') {
            throw new \Exception('微信接口报错:'.$arr['return_msg'],2022030);
        }
        if ($arr['result_code'] == 'FAIL') {
            throw new \Exception('微信接口报错:'.$arr['err_code'].'-'.$arr['err_code_des'],2022030);
        }
        $order = $orderPay->order;

        $refundObj = new OrderRefund();
        $refundObj->order_id = $orderPay->order_id;
        $refundObj->enterprise_id = $order->enterprise_id;
        $refundObj->shop_id = $order->shop_id;
        $refundObj->device_no = $deviceNo;
        $refundObj->openid = $orderPay->payer;
        $refundObj->refund_trade_no = $refund_trade_no;
        $refundObj->refund_trade_id = $arr['refund_id'];
        $refundObj->refund_fee = $refund_fee;
        $refundObj->pay_type = $orderPay->pay_type;
        $refundObj->ctime = time();
        if(!$refundObj->save(false)) throw new \Exception('数据保存失败',2021044);
        return $refundObj;
    }
    //退款查询
    public function refundquery(array $paras){

    }
    //撤销
    public function cancel(array $paras){
        throw new \Exception('支付超时，微信支付2.7版不支持撤销定单',2022010);
    }
    /**
     * 	作用：生成签名
     */
    public function getSign($Obj)
    {
        foreach ($Obj as $k => $v)
        {
            $Parameters[$k] = (string)$v;
        }
        //签名步骤一：按字典序排序参数
        ksort($Parameters, SORT_STRING);

        $String = $this->formatBizQueryParaMap($Parameters, false);
        //echo '【string1】'.$String.'</br>';
        //签名步骤二：在string后加入KEY
        $String = $String."&key=".$this->paymentObj->pay_sign_key;
        //echo "【string2】".$String."</br>";
        //签名步骤三：MD5加密
        $String = md5($String);
        //echo "【string3】 ".$String."</br>";
        //签名步骤四：所有字符转为大写
        $result_ = strtoupper($String);
        //echo "【result】 ".$result_."</br>";
        return $result_;
    }
    //验证支付成功通知sign
    public function checkNotifySign($arr)
    {
        $sign = $arr['sign'];
        unset($arr['sign']);
        return $sign == $this->getSign($arr);
    }

    public function getPayScript($order, $payDoneUrl = ''){
        $openid = $this->wxAccountObj->getWxOauthObj()->getOpenid();
        //微信支付商品标识
        $appObjWxpayGoods = AppBind::getObj(App::APP_WXPAY_GOODS, AppBind::TYPE_SHOP, $order->shop_id)->getUseObj()->getConfigObj();
        if ($appObjWxpayGoods->getAppState() && $appObjWxpayGoods->getgoods()) {
            $this->setPackage('goods_tag', $appObjWxpayGoods->getgoods());
        }
        //是否传设备号
        $appObjWxMicropay = $order->getAppUseObj(App::APP_WXPAY)->getConfigObj();
        $is_device = $appObjWxMicropay->getAttributeValue('is_device');
        if(!empty($is_device)) {
            $this->setPackage('device_info', (string)$order->device_no);
        }
        $this->setPackage('openid', $openid);
        $this->setPackage("trade_type","JSAPI");
        $this->setPackage("appid",$this->wxAccountObj->app_id);
        $this->setPackage("mch_id",$this->paymentObj->partner_id);
        $this->setPackage("nonce_str",Mcrypt::getcode(32));
        $this->setPackage("spbill_create_ip",$_SERVER['REMOTE_ADDR']);//终端ip
        $this->setPackage("sign",$this->getSign($this->packages));

        $this->getPrepayId();
        $jsApiParameters = $this->getParameters();
        if(empty($payDoneUrl)) $payDoneUrl = $order->getPayDoneUrl();

        return <<<EOF

function wxpay() {
    WeixinJSBridge.invoke('getBrandWCPayRequest', {$jsApiParameters}, function(res){
        // 返回 res.err_msg,取值
        // get_brand_wcpay_request:cancel 用户取消
        // get_brand_wcpay_request:fail 发送失败
        // get_brand_wcpay_request:ok 发送成功

        if(res.err_msg == "get_brand_wcpay_request:ok" ) {
            window.location = "{$payDoneUrl}";
        }else
        {
            //alert(res.err_msg);
        }
     });
}
EOF;

    }
    //储值卡js
    public function getTopUpScript($order, $package, $params = array()){
        $openid = $this->wxAccountObj->getWxOauthObj()->getOpenid();
        //微信支付商品标识
        $appObjWxpayGoods = AppBind::getObj(App::APP_WXPAY_GOODS, AppBind::TYPE_SHOP, $order->shop_id)->getUseObj()->getConfigObj();
        if ($appObjWxpayGoods->getAppState() && $appObjWxpayGoods->getgoods()) {
            $this->setPackage('goods_tag', $appObjWxpayGoods->getgoods());
        }
        $this->setPackage('openid', $openid);
        $this->setPackage("trade_type","JSAPI");
        $this->setPackage("appid",$this->wxAccountObj->app_id);
        $this->setPackage("mch_id",$this->paymentObj->partner_id);
        $this->setPackage("nonce_str",Mcrypt::getcode(32));
        $this->setPackage("spbill_create_ip",$_SERVER['REMOTE_ADDR']);//终端ip
        $this->setPackage("sign",$this->getSign($this->packages));

        $this->getPrepayId();
        $jsApiParameters = $this->getParameters();
        return <<<EOF
function wxpay() {
    WeixinJSBridge.invoke('getBrandWCPayRequest', {$jsApiParameters}, function(res){
        // 返回 res.err_msg,取值
        // get_brand_wcpay_request:cancel 用户取消
        // get_brand_wcpay_request:fail 发送失败
        // get_brand_wcpay_request:ok 发送成功

        if(res.err_msg == "get_brand_wcpay_request:ok" ) {
            window.location = "{$order->getPayDoneUrl()}";
        }else if(res.err_msg == "get_brand_wcpay_request:cancel" ){
        	history.go(-1);
        }else
        {
            alert(res.err_msg);
            history.go(-1);
        }
     });
}
EOF;

    }

    //定单查询并并重置支付成功结果，忽略非支付成功
   /* public function queryOrderAndRsetPaySuccess($order)
    {
        if(empty($order)) throw new \Exception('订单不存在',2021024);

        $result = $this->queryOrder($order->our_trade_no);
        if(empty($result)) throw new \Exception('微信接口返回数据为空',2022034);
        $result_arr = Func::xmlToArray($result);

        if($result_arr['return_code'] != 'SUCCESS') throw new \Exception('微信接口报错:'.$result_arr['return_msg'],2022030);
        if($result_arr['result_code'] != 'SUCCESS') throw new \Exception('微信接口报错:'.$result_arr['err_code_des'],2022030);
        switch($result_arr['trade_state'])
        {
            case "SUCCESS":
            {
                $pay_type = $result_arr['trade_type'] == 'MICROPAY' ? OrderPay::PAY_TYPE_WX_MICROPOS : OrderPay::PAY_TYPE_WX;
                $order->status = Order::STATUS_PAY_END;
                $order->pay_type = $pay_type;
                $order->ptime = strtotime($result_arr['time_end']);
                $order->saveRedis();
                $orderPay = OrderPay::getObjByOrderId($order->id);
                if(empty($orderPay)) {
                    $orderPay = new OrderPay();
                    $orderPay->order_id = $order->id;
                    $orderPay->enterprise_id = $order->enterprise_id;
                    $orderPay->shop_id = $order->shop_id;
                    $orderPay->device_no = $order->device_no;
                    $orderPay->cashier_id = $order->cashier_id;
                }
                $orderPay->pay_type = $pay_type;
                $orderPay->pay_fee = $result_arr['total_fee'];
                $orderPay->payee = $result_arr['mch_id'];
                $orderPay->payer = $result_arr['openid'];
                $orderPay->trade_id = $result_arr['transaction_id'];
                $orderPay->utime = strtotime($result_arr['time_end']);
                $orderPay->saveRedis();
                break;
            }
            case "PAYERROR":
            {
                break;
            }
            case "USERPAYING":
            {
                break;
            }
            default:
                break;
        }
        return $order;
    }*/
    //获取微信支付notify_url(支付成功通知url)
    public function getNotifyUrl()
    {
        return Yii::$app->urlManager->createAbsoluteUrl('pay/wxpaynotify');
    }

    /**
     * 	作用：设置标配的请求参数，生成签名，生成接口参数xml
     */
    function createXml()
    {
        return  Func::arrayToXml($this->packages);	}

    /**
     * 	作用：post请求xml
     */
    function postXml()
    {
        $xml = $this->createXml();
        $this->response = Func::postData($this->url, $xml);
        return $this->response;
    }

    /**
     * 	作用：获取结果，默认不使用证书
     */
    function getResult()
    {
        $this->postXml();
        $this->result = Func::xmlToArray($this->response);
        return $this->result;
    }

    /**
     * 获取prepay_id
     */
    function getPrepayId()
    {
        $this->result = $this->getResult();

        if(!isset($this->result['prepay_id']))
        {
            throw new \Exception($this->result['return_msg']);
        }
        $this->prepay_id = $this->result["prepay_id"];

        return $this->prepay_id;
    }
    /**
     * 	作用：设置jsapi的参数
     */
    public function getParameters()
    {
        $jsApiObj["appId"] = $this->wxAccountObj->app_id;
        $jsApiObj["timeStamp"] = (string)time();
        $jsApiObj["nonceStr"] = Mcrypt::getcode(32);
        $jsApiObj["package"] = "prepay_id=$this->prepay_id";
        $jsApiObj["signType"] = "MD5";
        $jsApiObj["paySign"] = $this->getSign($jsApiObj);
        $this->parameters = json_encode($jsApiObj);
        return $this->parameters;
    }


}